#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
#define ALLOCHTSIZE 7

typedef struct AllocHTItem{
	int *key;
	struct AllocHTItem* ptrnext;
} AllocHTItem;

typedef AllocHTItem* AllocHTable[HTSIZE];

AllocHTable* AllocTable; 

int nGlobalAlloc, nGlobalFree;
int AllochashCode ( int *key )
{
  
  return ( (long int)key % ALLOCHTSIZE );
} 

void AllocInit ()
{
//   _debug("Inituju alokacni HashTabulku");
  AllocTable = (AllocHTable*) malloc ( sizeof(AllocHTable) );

  for(int i=0; i<ALLOCHTSIZE; i++)
    (*AllocTable)[i]=NULL;
}


AllocHTItem* AllocSearch ( int *key )
{
  if(AllocTable == NULL)
    return NULL;

  AllocHTItem* Item=NULL;
  AllocHTItem* ptr = (*AllocTable)[AllochashCode(key)];
  while ( ptr != NULL )
    {
      if ( ptr->key == key )
        {
          Item = ptr;
          break;
        }
      ptr = ptr->ptrnext;
    }
  return Item;

}

void AllocInsert ( int *key )
{

  if(AllocTable == NULL)
    return;
  
  AllocHTItem* search = AllocSearch(key);
  if(search == NULL)
    {
      AllocHTItem* Item = (AllocHTItem*) malloc ( sizeof(AllocHTItem) );
      Item->key = key;
      Item->ptrnext = NULL;

      int Pos = AllochashCode(key);
      if((*AllocTable)[Pos] == NULL)
        (*AllocTable)[Pos] = Item;
      else
        {
          Item->ptrnext = (*AllocTable)[Pos];
          (*AllocTable)[Pos] = Item;
        }
    }
  else
    {
// 	je potreba doplnit !!! 
//       search->data = data;
    }
}

void AllocClearAll ( )
{
  if(AllocTable == NULL)
    return;

  AllocHTItem* Temp;
  for(int i=0; i<HTSIZE; i++)
  {
    while((*AllocTable)[i]!=NULL)
    {
      Temp = (*AllocTable)[i];
      (*AllocTable)[i]=(*AllocTable)[i]->ptrnext;
      free(Temp);
    }
  }
// //   free(AllocTable);
  return;
}


// funkce urcena k smazani pouze key
void AllocDelete ( int *key ) {
	
	if(AllocTable == NULL)
	  return;
	
	int hash = AllochashCode(key);
	AllocHTItem* ptr = (*AllocTable)[hash];
	AllocHTItem* node = NULL;
	// pokud neni prvek hash v poli NULL
	if(ptr != NULL){
	    // pokud je item na zacatku seznamu
	    if(ptr->key == key){
		node = ptr;
		// zajisti ze tam bude bud NULL nebo odkaz na dalsi ptr
		(*AllocTable)[hash] = node->ptrnext;
		free(node);
	    }else{
		// najdu si item s klicem a predchozi item
		while ( ptr != NULL ) {
		  
			// jestli dalsi prvek je NULL nesmim se divat na dalsi klic
			if(ptr->ptrnext != NULL){
			    if ( ptr->ptrnext->key == key ){
			      node = ptr->ptrnext;
			      break;
			    }
			}
			ptr = ptr->ptrnext;
		}
		// pokud neni node nulovy
		if(node != NULL){
		  ptr->ptrnext = node->ptrnext;
		  free(node);
		}
	    }
	}
}


void _debug_printAllocTable()
{
  int maxlen = 0;
  int sumcnt = 0;

  printf ("------------ALLOC TABLE--------------\n");
  for ( int i=0; i<HTSIZE; i++ )
    {
      printf ("%i:",i);
      int cnt = 0;
      AllocHTItem* ptr = (*AllocTable)[i];
      while ( ptr != NULL )
        {
          
          printf (" Key:%ld",(long int)ptr->key);
	  
          //if ( ptr != UNDEFPTR )
          cnt++;
          ptr = ptr->ptrnext;
        }
      printf ("\n");

      if (cnt > maxlen)
        maxlen = cnt;
      sumcnt+=cnt;
    }

  printf ("------------------------------------\n");
  printf ("Items count %i   The longest list %i\n",sumcnt,maxlen);
  printf ("------------------------------------\n");
}

void AllocClearMemmory(){
  AllocHTItem* node;
  for ( int i=0; i<HTSIZE; i++ )
    {
      
      AllocHTItem* ptr = (*AllocTable)[i];
      while ( ptr != NULL )
        {
          
	  
		node = ptr;
		ptr = ptr->ptrnext;
		free(node->key);
// 		free(node);
        }
      

      
    }
    _debug("Bylo allokovano %d a uvolneno %d", nGlobalAlloc, - nGlobalFree);
    AllocClearAll();
    free(AllocTable);
}

void* MyMalloc(size_t nSize, char *psFile, int nLine) {
    nGlobalAlloc++;
//     _debug("Allokace %s:%d", psFile, nLine);
    int *ptr;
    ptr = malloc(nSize);
    if (ptr == NULL) {
        _error("Chyba Mallocu!: Nedostatek pameti!\nAllok spusteny z: %s:%d\nProgram bude ukoncen.\n", psFile, nLine);
        exit(5);
        return NULL; 
    }
    else{  
	AllocInsert(ptr);
        return ptr;
    }
}


void* MyRealloc(void *mptr, size_t nSize, char *psFile, int nLine) {
//     _debug("ReAllokace");
    int *ptr;
    //ptr = malloc(nSize);
    ptr = realloc(mptr, nSize);
    AllocDelete(mptr);
    if (ptr == NULL) {
        _error("Chyba ReaLLocu!: Nedostatek pameti!\nAllok spusteny z: %s:%d\nProgram bude ukoncen.\n", psFile, nLine);
        exit(5);
        return NULL;
    }
    else{
	AllocInsert(ptr);
        return ptr;
    }
}
void  MyFree(void *ptr, char *psFile, int nLine){
    nGlobalFree--;
    AllocDelete(ptr);
    free(ptr);
//     _debug("pouzivam free %d %s:%d", ptr, psFile, nLine);
}

